package com.ruoyi.project.erp.service.impl;

import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.server.Sys;
import com.ruoyi.project.erp.domain.Project;
import com.ruoyi.project.erp.domain.Worklog;
import com.ruoyi.project.erp.domain.WorklogApproval;
import com.ruoyi.project.erp.domain.WorklogTemp;
import com.ruoyi.project.erp.dto.AssignWorklogDTO;
import com.ruoyi.project.erp.mapper.ProjectMapper;
import com.ruoyi.project.erp.mapper.WorklogApprovalMapper;
import com.ruoyi.project.erp.mapper.WorklogMapper;
import com.ruoyi.project.erp.mapper.WorklogTempMapper;
import com.ruoyi.project.erp.service.IWorklogTempService;
import com.ruoyi.project.erp.vo.DailyWorklogSummaryVO;
import com.ruoyi.project.erp.vo.WorklogItemVO;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class WorklogTempServiceImpl implements IWorklogTempService {

    @Autowired
    private WorklogMapper worklogMapper; // 假设已存在

    @Autowired
    private ProjectMapper projectMapper; // 假设已存在
    @Autowired
    private WorklogApprovalMapper worklogApprovalMapper; // 假设已存在

    @Autowired
    private WorklogTempMapper worklogTempMapper;
    @Autowired
    private SysDeptMapper deptMapper;
    private static final BigDecimal MAX_HOURS_PER_DAY = new BigDecimal("24.0");
    private static final int ALLOW_BACKDATE_DAYS = 7;

    @Override
    public WorklogTemp getTempWorklogById(Long id) {
        return worklogTempMapper.selectWorklogTempById(id);
    }

    @Override
    public DailyWorklogSummaryVO getDailySummary(Date workDate) {

        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        // String userId = user.getDdUserId(); // 这里的 userId 类型和 mapper 方法参数不匹配，应该是 Long
        String userId = user.getDdUserId(); // 假设 worklog 和 worklog_temp 的 user_id 存的是 sys_user.user_id

        DailyWorklogSummaryVO summary = new DailyWorklogSummaryVO();

        // 1. 查询暂存工时明细列表 (这部分逻辑不变)
        List<WorklogItemVO> tempItems = worklogTempMapper.selectTempWorklogItemsVO(userId, workDate);
        summary.setTempItems(tempItems);
        log.debug("查到的暂存：{}", tempItems);

        // 2. 查询已提交工时明细列表 (这部分逻辑不变，查询出所有状态的工时)
        List<WorklogItemVO> submittedItems = worklogMapper.selectSubmittedWorklogItemsVO(userId, workDate);
        summary.setSubmittedItems(submittedItems); // 将【完整的列表】设置给VO，用于前端展示

        // 3. 基于查询到的列表在内存中进行计算
        BigDecimal tempHours = (tempItems == null) ? BigDecimal.ZERO : tempItems.stream()
                .map(WorklogItemVO::getHours)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 【核心修改点】在计算 assignedHours 时，过滤掉无效状态
        // 我们需要一个稳定的状态字段来判断，假设 final_status 是 'pending', 'archived', 'rejected'
        // 我们需要在 WorklogItemVO 中有 finalStatus 字段。
        BigDecimal assignedHours = (submittedItems == null) ? BigDecimal.ZERO : submittedItems.stream()
                // 在 map 之前，先 filter 过滤
                .filter(item -> !"rejected".equals(item.getStatus()) && !"returned".equals(item.getStatus())) // 假设还有“已回退(retracted)”状态也不计入
                .map(WorklogItemVO::getHours)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal totalHours = assignedHours.add(tempHours);
        BigDecimal remainingHours = MAX_HOURS_PER_DAY.subtract(totalHours).max(BigDecimal.ZERO);

        // 4. 将【计算后的结果】设置回 VO 对象 (这部分逻辑不变)
        summary.setAssignedHours(assignedHours);
        summary.setTempHours(tempHours);
        summary.setTotalHours(totalHours);
        summary.setRemainingHours(remainingHours);

        return summary;
    }

    @Override
    @Transactional
    public AjaxResult saveOrUpdateTempWorklog(WorklogTemp worklogTemp) {
        // 1. 基本信息和校验 (这部分不变)
        validateOvertime(worklogTemp);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = loginUser.getUser();
        String userId = user.getDdUserId();
        String username = SecurityUtils.getUsername();
        worklogTemp.setUserId(userId);
        validateWorkDate(worklogTemp.getWorkDate());
        // 前置校验：必须选择一个项目 (这部分不变)
        if (StringUtils.isEmpty(worklogTemp.getProjectId())) {
            throw new ServiceException("必须选择一个项目");
        }
        // 2. 区分新增和修改
        if (worklogTemp.getId() == null) { // 新增逻辑

            // 【修改点 A】: 只有在提交的工时带有任务ID时，才进行唯一性校验
            if (worklogTemp.getTaskId() != null) {
                WorklogTemp existLog = worklogTempMapper.selectWorklogTempUnique(
                        userId,
                        worklogTemp.getProjectId(),
                        worklogTemp.getTaskId(),
                        worklogTemp.getWorkDate()
                );
                if (existLog != null) {
                    // 如果是带任务的，并且已存在，则报错
                    throw new ServiceException("您当天已为该任务暂存了工时，请勿重复添加。");
                }
            }
            // 如果不带任务ID (taskId is null)，则直接跳过校验，允许用户添加多条
            validateTotalHours(userId, worklogTemp.getWorkDate(), worklogTemp.getHours(), null);
            worklogTemp.setCreateBy(username);
            worklogTempMapper.insertWorklogTemp(worklogTemp);
            return AjaxResult.success("暂存成功");
        } else { // 修改逻辑

            WorklogTemp oldLog = worklogTempMapper.selectWorklogTempById(worklogTemp.getId());
            if (oldLog == null || !oldLog.getUserId().equals(userId)) {
                throw new ServiceException("记录不存在或您无权修改");
            }

            validateTotalHours(userId, worklogTemp.getWorkDate(), worklogTemp.getHours(), oldLog.getHours());

            // 【修改点 B】: 这里是整个改造的核心，从“检查冲突”升级为“智能合并”
            // 只有当用户为记录分配了任务时，才有可能触发合并
            if (worklogTemp.getTaskId() != null) {
                WorklogTemp mergeTargetLog = worklogTempMapper.selectWorklogTempUnique(
                        userId,
                        worklogTemp.getProjectId(),
                        worklogTemp.getTaskId(),
                        worklogTemp.getWorkDate());

                // 如果找到了一个合并目标，并且它不是我们正在修改的这条记录本身
                if (mergeTargetLog != null && !mergeTargetLog.getId().equals(worklogTemp.getId())) {
                    // 【执行智能合并逻辑】
                    // 1. 合并工时
                    BigDecimal totalHours = mergeTargetLog.getHours().add(worklogTemp.getHours());
                    mergeTargetLog.setHours(totalHours);

                    // 2. 合并加班信息
                    if (worklogTemp.getIsOvertime() != null && worklogTemp.getIsOvertime() == 1) {
                        mergeTargetLog.setIsOvertime(1);
                    }
                    BigDecimal sourceOvertime = worklogTemp.getOvertimeHours() == null ? BigDecimal.ZERO : worklogTemp.getOvertimeHours();
                    BigDecimal targetOvertime = mergeTargetLog.getOvertimeHours() == null ? BigDecimal.ZERO : mergeTargetLog.getOvertimeHours();
                    mergeTargetLog.setOvertimeHours(targetOvertime.add(sourceOvertime));

                    // 3. 合并工作内容
                    if (StringUtils.isNotEmpty(worklogTemp.getContent())) {
                        String oldContent = StringUtils.isNotEmpty(mergeTargetLog.getContent()) ? mergeTargetLog.getContent() + "；" : "";
                        mergeTargetLog.setContent(oldContent + worklogTemp.getContent());
                    }

                    // 4. 合并备注
                    if (StringUtils.isNotEmpty(worklogTemp.getRemark())) {
                        StringBuilder finalRemark = new StringBuilder();
                        if (StringUtils.isNotEmpty(mergeTargetLog.getRemark())) {
                            finalRemark.append(mergeTargetLog.getRemark()).append("\n");
                        }
                        finalRemark.append("[合并备注]: ").append(worklogTemp.getRemark());
                        mergeTargetLog.setRemark(finalRemark.toString());
                    }

                    mergeTargetLog.setUpdateBy(username);
                    worklogTempMapper.updateWorklogTemp(mergeTargetLog);

                    // 5. 删除被合并的原始记录
                    worklogTempMapper.deleteWorklogTempById(oldLog.getId());

                    return AjaxResult.success("修改成功，工时已自动合并。");
                }
            }

            // 【修改点 C】: 如果没有发生合并，则执行常规的更新逻辑
            // 这段代码就是你原来的更新代码，现在它成了不合并时的“else”分支
            oldLog.setProjectId(worklogTemp.getProjectId());
            oldLog.setTaskId(worklogTemp.getTaskId());
            oldLog.setHours(worklogTemp.getHours());
            oldLog.setContent(worklogTemp.getContent());
            oldLog.setIsOvertime(worklogTemp.getIsOvertime());
            oldLog.setOvertimeHours(worklogTemp.getOvertimeHours());
            oldLog.setRemark(worklogTemp.getRemark()); // 直接覆盖备注
            oldLog.setUpdateBy(username);

            worklogTempMapper.updateWorklogTemp(oldLog);
            return AjaxResult.success("修改成功");
        }
    }

    @Override
    @Transactional
    public AjaxResult assignTempWorklog(AssignWorklogDTO dto) {
        WorklogTemp tempLog = worklogTempMapper.selectWorklogTempById(dto.getTempWorklogId());
        if (tempLog == null) {
            throw new ServiceException("要分配的临时工时记录不存在或已被分配");
        }
        if (!SecurityUtils.getUserId().equals(tempLog.getUserId())) {
            throw new ServiceException("只能分配自己的工时");
        }

        BigDecimal totalAssignHours = dto.getAssignments().stream()
                .map(Worklog::getHours)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        if (tempLog.getHours().compareTo(totalAssignHours) != 0) {
            throw new ServiceException("分配的总工时(" + totalAssignHours + ")必须等于暂存的工时(" + tempLog.getHours() + ")");
        }

        String username = SecurityUtils.getUsername();
        for (Worklog assignment : dto.getAssignments()) {
            Worklog newLog = new Worklog();
            newLog.setProjectId(tempLog.getProjectId());
            newLog.setTaskId(assignment.getTaskId());
            newLog.setUserId(tempLog.getUserId());
            newLog.setWorkDate(tempLog.getWorkDate());
            newLog.setHours(assignment.getHours());
            newLog.setContent(assignment.getContent());

            if (tempLog.getIsOvertime() != null && tempLog.getIsOvertime() == 1) {
                newLog.setIsOvertime(1);
                BigDecimal assignedOvertime = assignment.getHours()
                        .divide(tempLog.getHours(), 10, BigDecimal.ROUND_HALF_UP)
                        .multiply(tempLog.getOvertimeHours())
                        .setScale(1, BigDecimal.ROUND_HALF_UP);
                newLog.setOvertimeHours(assignedOvertime);
            } else {
                newLog.setIsOvertime(0);
                newLog.setOvertimeHours(BigDecimal.ZERO);
            }

            newLog.setCreateBy(username);
            newLog.setCreateTime(new Date());
            worklogMapper.insertWorklog(newLog); // 假设worklogMapper已有标准的insertWorklog方法
        }

        worklogTempMapper.deleteWorklogTempById(dto.getTempWorklogId());

        return AjaxResult.success("工时分配成功");
    }

    @Override
    public List<WorklogTemp> selectCurrentUserPendingList() {
        WorklogTemp queryParam = new WorklogTemp();
        LoginUser user = SecurityUtils.getLoginUser();
        SysUser sysUser = user.getUser();
        queryParam.setUserId(sysUser.getDdUserId());
        return worklogTempMapper.selectWorklogTempList(queryParam);
    }

    @Override
    @Transactional // <--- 关键点1：添加事务注解，保证数据一致性
    public AjaxResult submitTempWorklogsByDate(Date workDate) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser sysUser = loginUser.getUser();
        // 建议在整个系统里统一使用 Long 类型的 userId，而不是 String 的 dd_user_id
        String userId = sysUser.getDdUserId();
        String username = loginUser.getUsername();
        Long deptId = sysUser.getDeptId();
        if (deptId == null) {
            throw new ServiceException("无法找到您的部门信息，无法创建审批流。");
        }
        SysDept dept = deptMapper.selectDeptById(deptId);
        if (dept == null || StringUtils.isEmpty(dept.getLeader())) {
            throw new ServiceException("您所在的部门未设置负责人，无法创建审批流。");
        }
        // 将部门负责人ID提前获取，避免在循环中重复查询
        String leaderId = dept.getLeader();
        // 【核心校验】: 检查当天是否有未分配任务的暂存工时
        // 假设 WorklogTemp 表中有一个 task_id 字段
        int unassignedCount = worklogTempMapper.countUnassignedByDate(userId, workDate);
        if (unassignedCount > 0) {
            return AjaxResult.error("您有 " + unassignedCount + " 条工时未分配到具体任务，请先分配再提交。");
        }


        int unassignedCount2 = worklogTempMapper.countUnassignedWorkStyleByDate(userId, workDate);
        if (unassignedCount2 > 0) {
            return AjaxResult.error("您有 " + unassignedCount2 + " 条工时未分配工作方式，请先分配再提交。");
        }


        int unassignedCount3 = worklogTempMapper.countUnassignedClosedLoopByDate(userId, workDate);
        if (unassignedCount3 > 0) {
            return AjaxResult.error("您有 " + unassignedCount3 + " 条工时未填写是否闭环，请先分配再提交。");
        }
        // 查找当天所有已分配任务的暂存记录
        List<WorklogTemp> tempList = worklogTempMapper.selectListForSubmit(userId, workDate);
        if (tempList == null || tempList.isEmpty()) {
            return AjaxResult.error("没有可提交的工时记录。");
        }

        // --- 逻辑重构开始 ---

        List<Worklog> worklogsToInsert = new ArrayList<>();
        List<Long> tempIdsToDelete = new ArrayList<>();

        // 1. 遍历暂存记录，转换为 Worklog 对象列表
        for (WorklogTemp temp : tempList) {
            // 在 selectListForSubmit 查询时就应该过滤掉 taskId is null 的情况，这里是双重保险
            if (temp.getTaskId() == null) {
                throw new ServiceException("内部错误：存在未分配到任务的工时，无法提交！");
            }

            Worklog worklog = new Worklog();
            worklog.setProjectId(temp.getProjectId());
            worklog.setTaskId(temp.getTaskId());
            worklog.setUserId(userId);
            worklog.setWorkDate(workDate);
            worklog.setHours(temp.getHours());
            worklog.setContent(temp.getContent());
            worklog.setIsOvertime(temp.getIsOvertime());
            worklog.setOvertimeHours(temp.getOvertimeHours());
            worklog.setFinalStatus("pending");
            worklog.setStatus("pending"); // 假设 "0" 是待审批
            worklog.setWorkStyle(temp.getWorkStyle());
            worklog.setClosedLoop(temp.getClosedLoop());
            if (dept.getLeader().equals(userId)){
                worklog.setApprovalStep(2L);
            }else{
                worklog.setApprovalStep(1L);
            }

            worklog.setCreateTime(new Date());

            worklogsToInsert.add(worklog);
            tempIdsToDelete.add(temp.getId());
        }

        // 2. 批量插入 worklog 表。执行后，worklogsToInsert 中每个 worklog 对象的 ID 将被回填
        if (worklogsToInsert.isEmpty()) {
            return AjaxResult.error("没有可提交的工时记录。"); // 再次校验
        }
        worklogMapper.batchInsertWorklog(worklogsToInsert);

        // 3. 现在 worklog 对象已经有 ID 了，可以创建审批流记录
        List<WorklogApproval> approvalsToInsert = new ArrayList<>();


        for (Worklog worklog : worklogsToInsert) {
            WorklogApproval worklogApproval = new WorklogApproval();
            // <--- 关键点2：在这里获取ID，此时ID已经有值了
            worklogApproval.setWorklogId(worklog.getId());
            Project project = projectMapper.selectProjectInfoByProjectId(worklog.getProjectId());
            if (dept.getLeader().equals(userId)){
                worklogApproval.setStep(2L);
                worklogApproval.setApproverId(project.getSaleId());
            }else{
                worklogApproval.setStep(1L);
            }
            worklogApproval.setApproverId(leaderId); // 使用提前获取的leaderId
            worklogApproval.setStatus("pending");
            worklogApproval.setCreateTime(DateUtils.getNowDate());
            approvalsToInsert.add(worklogApproval);
        }

        // 4. 批量插入审批记录
        if (!approvalsToInsert.isEmpty()) {
            worklogApprovalMapper.batchInsertWorklogApproval(approvalsToInsert);
        }

        // 5. 批量删除已处理的 worklog_temp 记录
        if (!tempIdsToDelete.isEmpty()) {
            worklogTempMapper.deleteWorklogTempByIds(tempIdsToDelete.toArray(new Long[0]));
        }

        return AjaxResult.success("工时提交成功！");
    }

    @Override
    public int deleteWorklogTempById(Long id) {
        return worklogTempMapper.deleteWorklogTempById(id);
    }

    // --- 私有校验方法 ---

    private void validateOvertime(WorklogTemp worklogTemp) {
        if (worklogTemp.getHours() == null || worklogTemp.getHours().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ServiceException("总工时必须大于0");
        }
        if (worklogTemp.getIsOvertime() == null || worklogTemp.getIsOvertime() == 0) {
            worklogTemp.setIsOvertime(0);
            worklogTemp.setOvertimeHours(BigDecimal.ZERO);
        } else {
            if (worklogTemp.getOvertimeHours() == null || worklogTemp.getOvertimeHours().compareTo(BigDecimal.ZERO) <= 0) {
                throw new ServiceException("加班工时必须大于0");
            }
            if (worklogTemp.getOvertimeHours().compareTo(worklogTemp.getHours()) > 0) {
                throw new ServiceException("加班工时不能大于总工时");
            }
        }
    }

    private void validateWorkDate(Date workDate) {
        if (workDate == null) {
            throw new ServiceException("工作日期不能为空");
        }
        LocalDate workLocalDate = workDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        if (ChronoUnit.DAYS.between(workLocalDate, LocalDate.now()) >= ALLOW_BACKDATE_DAYS) {
            throw new ServiceException("只能补录7天内的工时");
        }
    }

    private void validateTotalHours(String userId, Date workDate, BigDecimal newHours, BigDecimal oldHours) {
        BigDecimal assignedHours = worklogMapper.sumAssignedHoursByUserIdAndDate(userId, workDate);
        assignedHours = (assignedHours == null) ? BigDecimal.ZERO : assignedHours;

        BigDecimal tempHours = worklogTempMapper.sumTempHoursByUserIdAndDate(userId, workDate);
        tempHours = (tempHours == null) ? BigDecimal.ZERO : tempHours;

        // 如果是更新，要先从总数里减去此条记录的旧工时
        if (oldHours != null) {
            tempHours = tempHours.subtract(oldHours);
        }

        BigDecimal totalHoursAfterSave = assignedHours.add(tempHours).add(newHours);
        if (totalHoursAfterSave.compareTo(MAX_HOURS_PER_DAY) > 0) {
            throw new ServiceException("当天总工时（已分配+暂存）不能超过24小时！");
        }
    }
}
